﻿using AIStudio.Wpf.DiagramDesigner.Additionals.Commands;
using AIStudio.Wpf.Flowchart;
using AIStudio.Wpf.DiagramDesigner.Additionals.Extensions.ViewModels;
using AIStudio.Wpf.DiagramDesigner.Additionals;
using AIStudio.Wpf.DiagramApp.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Serialization;
using AIStudio.Wpf.DiagramDesigner;
using ZXing;
using AIStudio.Wpf.DiagramDesigner.Helpers;
using AIStudio.Wpf.DiagramDesigner.ViewModels;
using AIStudio.Wpf.DiagramDesigner.ViewModels.BaseViewModel;

namespace AIStudio.Wpf.DiagramApp.ViewModels
{
    public partial class PageViewModel : BindableBase
    {
        protected IDiagramServiceProvider _service
        {
            get
            {
                return DiagramServicesProvider.Instance.Provider;
            }
        }

        public PageViewModel(string title, string status, DiagramType diagramType, string subType = null)
        {
            Title = title;
            Status = status;
            DiagramType = diagramType;
            SubType = subType;
            Init(true);
        }
        public PageViewModel(string filename)
        {
            FileName = filename;
            string ext = Path.GetExtension(filename);
            var diagramDocument = OpenFile(filename, ext);
            OpenFile(diagramDocument, ext);
        }

        public PageViewModel(string filename, DiagramDocument diagramDocument)
        {
            FileName = filename;
            string ext = Path.GetExtension(filename);
            OpenFile(diagramDocument, ext);
        }

        protected virtual void InitDiagramViewModel()
        {

        }

        protected virtual void Init(bool initNew)
        {
            DiagramViewModels = new ObservableCollection<IDiagramViewModel>()
            {
                GetDiagramViewModel("页-1", DiagramType,initNew),
            };
            DiagramViewModel = DiagramViewModels.FirstOrDefault();

            InitDiagramViewModel();
        }

        public string FileName
        {
            get; set;
        }

        #region 属性

        private string _title;
        public string Title
        {
            get
            {
                return _title;
            }
            set
            {
                SetProperty(ref _title, value);
            }
        }

        private string _status;
        public string Status
        {
            get
            {
                return _status;
            }
            set
            {
                SetProperty(ref _status, value);
            }
        }

        private bool _showGrid;
        public bool ShowGrid
        {
            get
            {
                return _showGrid;
            }
            set
            {
                if (SetProperty(ref _showGrid, value))
                {
                    foreach (var item in DiagramViewModels)
                    {
                        item.ShowGrid = _showGrid;
                    }
                }
            }
        }

        public DiagramType DiagramType
        {
            get; set;
        }

        public string SubType
        {
            get; set;
        }

        private ObservableCollection<IDiagramViewModel> _diagramViewModels;
        public ObservableCollection<IDiagramViewModel> DiagramViewModels
        {
            get
            {
                return _diagramViewModels;
            }
            set
            {
                SetProperty(ref _diagramViewModels, value);
            }
        }

        private IDiagramViewModel _diagramViewModel;
        public IDiagramViewModel DiagramViewModel
        {
            get
            {
                return _diagramViewModel;
            }
            set
            {
                if (_diagramViewModel != value)
                {
                    if (_diagramViewModel != null)
                    {
                        _diagramViewModel.PropertyChanged -= DiagramViewModel_PropertyChanged;
                    }
                    SetProperty(ref _diagramViewModel, value);
                    if (_diagramViewModel != null)
                    {
                        _diagramViewModel.PropertyChanged += DiagramViewModel_PropertyChanged;
                    }
                }
            }
        }
        #endregion

        #region 

        private void DiagramViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsSelected")
            {
                _service.SelectedItems = DiagramViewModel?.SelectedItems;

                _service.SelectedItem = DiagramViewModel?.SelectedItem;
            }

            var property = sender.GetType().GetProperty(e.PropertyName);
            var attr = property.GetCustomAttributes(typeof(BrowsableAttribute), true);
            if (attr != null && attr.Length != 0 && (attr[0] as BrowsableAttribute).Browsable == false)
            {
                return;
            }

            Status = "*";
        }

        #endregion

        protected virtual bool AddVerify(SelectableDesignerItemViewModelBase arg)
        {
            return true;
        }

        public static DiagramDocument OpenFile(string filename, string ext)
        {
            try
            {
                DiagramDocument diagramDocument = null;

                if (ext == ".xml")
                {
                    XmlSerializer serializer = new XmlSerializer(typeof(DiagramDocument));
                    FileInfo fileInfo = new FileInfo(filename);

                    using (TextReader reader = fileInfo.OpenText())
                    {
                        diagramDocument = (DiagramDocument)serializer.Deserialize(reader);
                    }
                }
                else
                {
                    diagramDocument = JsonConvert.DeserializeObject<DiagramDocument>(File.ReadAllText(filename));
                }

                return diagramDocument;
            }
            catch (System.IO.FileNotFoundException fnfe)
            {
                throw new FileNotFoundException("The system document could not be found ", fnfe);
            }
            catch (System.IO.DirectoryNotFoundException dnfe)
            {
                throw new DirectoryNotFoundException("A required directory was nt found", dnfe);
            }
            catch (System.IO.IOException ioe)
            {
                throw new IOException("A file system error occurred", ioe);
            }
            catch (System.UnauthorizedAccessException uae)
            {
                throw new UnauthorizedAccessException("The requested file system access wasnot granted", uae);
            }
            catch (System.Security.SecurityException se)
            {
                throw new System.Security.SecurityException("The security policy prevents access to a file system resource", se);
            }
            catch (System.Exception e)
            {
                throw new System.Exception(
                    string.Format("The database format vc  invalid \r\n Exception:{0} \r\n InnerException:{1}", e.Message, e.InnerException.Message));
            }
        }

        protected virtual void OpenFile(DiagramDocument diagramDocument, string ext)
        {
            Title = diagramDocument.Title;
            DiagramType = diagramDocument.DiagramType;

            List<DiagramViewModel> viewModels = new List<DiagramViewModel>();
            foreach (var diagramItem in diagramDocument.DiagramItems)
            {
                var viewModel = GetDiagramViewModel(diagramItem.Name, diagramItem.DiagramType, false);
                viewModel.ShowGrid = diagramItem.ShowGrid;
                viewModel.PhysicalGridCellSize = diagramItem.PhysicalGridCellSize;
                viewModel.CellHorizontalAlignment = diagramItem.CellHorizontalAlignment;
                viewModel.CellVerticalAlignment = diagramItem.CellVerticalAlignment;
                viewModel.PageSizeOrientation = diagramItem.PageSizeOrientation;
                viewModel.PhysicalPageSize = diagramItem.PhysicalPageSize;
                viewModel.PageSizeType = diagramItem.PageSizeType;
                viewModel.PhysicalGridMarginSize = diagramItem.PhysicalGridMarginSize;
                viewModel.GridColor = diagramItem.GridColor;
                viewModel.AllowDrop = diagramItem.AllowDrop;

                foreach (var diagramItemData in diagramItem.DesignerItems)
                {
                    Type type = TypeHelper.GetType(diagramItemData.ModelTypeName);
                    DesignerItemViewModelBase itemBase = Activator.CreateInstance(type, viewModel, diagramItemData, ext) as DesignerItemViewModelBase;
                    viewModel.Items.Add(itemBase);
                }

                foreach (var connection in diagramItem.Connections)
                {
                    Type type = TypeHelper.GetType(connection.SerializableTypeName);
                    var connectionItem = SerializeHelper.DeserializeObject(type, connection.SerializableString, ext) as ConnectionItem;

                    connectionItem.SourceType = System.Type.GetType(connectionItem.SourceTypeName);
                    connectionItem.SinkType = System.Type.GetType(connectionItem.SinkTypeName);
                    DesignerItemViewModelBase sourceItem = DiagramViewModelHelper.GetConnectorDataItem(viewModel.Items, connectionItem.SourceId, connectionItem.SourceType);
                    ConnectorOrientation sourceConnectorOrientation = connectionItem.SourceOrientation;
                    FullyCreatedConnectorInfo sourceConnectorInfo = sourceItem.GetFullConnectorInfo(connectionItem.Id, sourceConnectorOrientation, connectionItem.SourceXRatio, connectionItem.SourceYRatio, connectionItem.SourceInnerPoint, connectionItem.SourceInnerPoint);

                    DesignerItemViewModelBase sinkItem = DiagramViewModelHelper.GetConnectorDataItem(viewModel.Items, connectionItem.SinkId, connectionItem.SinkType);
                    ConnectorOrientation sinkConnectorOrientation = connectionItem.SinkOrientation;
                    FullyCreatedConnectorInfo sinkConnectorInfo = sinkItem.GetFullConnectorInfo(connectionItem.Id, sinkConnectorOrientation, connectionItem.SinkXRatio, connectionItem.SinkYRatio, connectionItem.SinkInnerPoint, connectionItem.SinkInnerPoint);

                    ConnectionViewModel connectionVM = new ConnectionViewModel(viewModel, sourceConnectorInfo, sinkConnectorInfo, connectionItem);
                    connectionVM.Id = Guid.NewGuid();
                    viewModel.Items.Add(connectionVM);
                }

                viewModels.Add(viewModel);
            }
            DiagramViewModels = new ObservableCollection<IDiagramViewModel>(viewModels);
            DiagramViewModel = DiagramViewModels.FirstOrDefault();
        }

        public bool SaveFile(bool isSaveAs = false)
        {
            string filter = "Files (*.xml)|*.xml|Files (*.json)|*.json|All Files (*.*)|*.*";

            if (string.IsNullOrEmpty(FileName) || isSaveAs == true)
            {
                Microsoft.Win32.SaveFileDialog saveFile = new Microsoft.Win32.SaveFileDialog();
                saveFile.Filter = filter;
                if (saveFile.ShowDialog() == true)
                {
                    FileName = saveFile.FileName;
                    Title = Path.GetFileNameWithoutExtension(FileName);
                }
                else
                {
                    return false;
                }
            }

            var ext = Path.GetExtension(FileName);

            DiagramDocument diagramDocument = new DiagramDocument();
            diagramDocument.DiagramItems = new List<DiagramItem>();
            diagramDocument.Title = Title;
            diagramDocument.DiagramType = DiagramType;

            foreach (var viewModel in DiagramViewModels)
            {
                DiagramItem diagramItem = new DiagramItem(viewModel);

                var selectedDesignerItems = viewModel.Items.OfType<DesignerItemViewModelBase>();
                var selectedConnections = viewModel.Items.OfType<ConnectionViewModel>();

                diagramItem.DesignerItems = selectedDesignerItems.Select(p => p.ToSerializableItem(ext)).Where(p => p != null).ToList();
                diagramItem.Connections = selectedConnections.Select(p => p.ToSerializableItem(ext)).Where(p => p != null).ToList();

                diagramDocument.DiagramItems.Add(diagramItem);
            }

            if (ext == ".xml")
            {
                FileInfo file = new FileInfo(FileName);
                diagramDocument.Save(file);
            }
            else
            {
                File.WriteAllText(FileName, JsonConvert.SerializeObject(diagramDocument));
            }
            Status = "";

            return true;
        }

        private bool ItemsToDeleteHasConnector(List<SelectableDesignerItemViewModelBase> itemsToRemove, ConnectorInfoBase connector)
        {
            if (connector is FullyCreatedConnectorInfo fully)
            {
                return itemsToRemove.Contains(fully.DataItem);
            }

            return false;
        }

        #region 主题
        public void SetPropertyValue(SelectableDesignerItemViewModelBase selectable, string propertyName)
        {
            foreach (var item in DiagramViewModel.SelectedItems)
            {
                if (item != selectable)
                {
                    CopyHelper.CopyPropertyValue(selectable, item, propertyName);
                }
            }
        }

        public void SetFont(IFontViewModel fontViewModel, string propertyName)
        {
            foreach (var item in DiagramViewModel.SelectedItems)
            {
                if (item.FontViewModel != fontViewModel)
                {
                    CopyHelper.CopyPropertyValue(fontViewModel, item.FontViewModel, propertyName);
                }
            }
        }

        public void SetColor(IColorViewModel colorViewModel, string propertyName)
        {
            foreach (var item in DiagramViewModel.SelectedItems)
            {
                if (item.ColorViewModel != colorViewModel)
                {
                    CopyHelper.CopyPropertyValue(colorViewModel, item.ColorViewModel, propertyName);
                }
            }
        }

        public void SetSharp(IShapeViewModel shapeViewModel, string propertyName)
        {
            foreach (var item in DiagramViewModel.SelectedItems)
            {
                if (item.ShapeViewModel != shapeViewModel)
                {
                    CopyHelper.CopyPropertyValue(shapeViewModel, item.ShapeViewModel, propertyName);
                }
            }
        }

        public void SetQuickItem(IQuickThemeViewModel quickThemeViewModel, string propertyName)
        {
            if (propertyName == nameof(QuickTheme) && quickThemeViewModel.QuickTheme != null)
            {
                foreach (var item in DiagramViewModel.SelectedItems)
                {
                    SetFont(quickThemeViewModel.QuickTheme.FontViewModel, "FontColor");
                    SetColor(quickThemeViewModel.QuickTheme.ColorViewModel, "FillColor");
                    SetColor(quickThemeViewModel.QuickTheme.ColorViewModel, "LineColor");
                    SetColor(quickThemeViewModel.QuickTheme.ColorViewModel, "LineWidth");
                }
                quickThemeViewModel.QuickTheme = null;
            }
        }

        public void LockAction(LockObject lockObject, string propertyName)
        {
            foreach (var item in DiagramViewModel?.SelectedItems)
            {
                item.LockObjectViewModel.SetValue(lockObject);
            }
        }

        public virtual void AddPageExecuted(object para)
        {
            int index = 0;
            if (para is DiagramViewModel oldpage)
            {
                index = DiagramViewModels.IndexOf(oldpage) + 1;
            }
            else
            {
                index = DiagramViewModels.Count;
            }
            var page = GetDiagramViewModel(null, DiagramType, true);
            DiagramViewModels.Insert(index, page);
            DiagramViewModel = page;
            InitDiagramViewModel();
        }

        protected virtual DiagramViewModel GetDiagramViewModel(string name, DiagramType diagramType, bool initNew)
        {
            return new DiagramViewModel() { Name = name ?? NewNameHelper.GetNewName(DiagramViewModels.Select(p => p.Name), "页-"), DiagramType = diagramType };
        }

        public void AddCopyPageExecuted(object para)
        {
            if (DiagramViewModel != null)
            {
                var viewModel = DiagramViewModel;
                DiagramItem diagramItem = new DiagramItem(viewModel);

                var selectedDesignerItems = viewModel.Items.OfType<DesignerItemViewModelBase>();
                var selectedConnections = viewModel.Items.OfType<ConnectionViewModel>();

                diagramItem.DesignerItems = selectedDesignerItems.Select(p => p.ToSerializableItem("json")).Where(p => p != null).ToList();
                diagramItem.Connections = selectedConnections.Select(p => p.ToSerializableItem("json")).Where(p => p != null).ToList();

                viewModel = new DiagramViewModel(diagramItem);
                viewModel.Name = NewNameHelper.GetNewName(DiagramViewModels.Select(p => p.Name), "页-");

                foreach (var diagramItemData in diagramItem.DesignerItems)
                {
                    Type type = TypeHelper.GetType(diagramItemData.ModelTypeName);
                    DesignerItemViewModelBase itemBase = Activator.CreateInstance(type, viewModel, diagramItemData, "json") as DesignerItemViewModelBase;
                    viewModel.Items.Add(itemBase);
                }

                foreach (var connection in diagramItem.Connections)
                {
                    var connectionItem = JsonConvert.DeserializeObject<ConnectionItem>(connection.SerializableString);

                    connectionItem.SourceType = System.Type.GetType(connectionItem.SourceTypeName);
                    connectionItem.SinkType = System.Type.GetType(connectionItem.SinkTypeName);
                    DesignerItemViewModelBase sourceItem = DiagramViewModelHelper.GetConnectorDataItem(viewModel.Items, connectionItem.SourceId, connectionItem.SourceType);
                    ConnectorOrientation sourceConnectorOrientation = connectionItem.SourceOrientation;
                    FullyCreatedConnectorInfo sourceConnectorInfo = sourceItem.GetFullConnectorInfo(connectionItem.Id, sourceConnectorOrientation, connectionItem.SourceXRatio, connectionItem.SourceYRatio, connectionItem.SourceInnerPoint, connectionItem.SourceIsPortless);

                    DesignerItemViewModelBase sinkItem = DiagramViewModelHelper.GetConnectorDataItem(viewModel.Items, connectionItem.SinkId, connectionItem.SinkType);
                    ConnectorOrientation sinkConnectorOrientation = connectionItem.SinkOrientation;
                    FullyCreatedConnectorInfo sinkConnectorInfo = sinkItem.GetFullConnectorInfo(connectionItem.Id, sinkConnectorOrientation, connectionItem.SinkXRatio, connectionItem.SinkYRatio, connectionItem.SinkInnerPoint, connectionItem.SinkIsPortless);

                    ConnectionViewModel connectionVM = new ConnectionViewModel(viewModel, sourceConnectorInfo, sinkConnectorInfo, connectionItem);
                    connectionVM.Id = Guid.NewGuid();
                    viewModel.Items.Add(connectionVM);
                }

                DiagramViewModels.Add(viewModel);
                DiagramViewModel = viewModel;
                InitDiagramViewModel();
            }
        }

        public void DeletePageExecuted(object para)
        {
            if (para is DiagramViewModel oldpage)
            {
                int index = DiagramViewModels.IndexOf(oldpage) - 1;
                DiagramViewModels.Remove(oldpage);
                if (index > 0)
                {
                    DiagramViewModel = DiagramViewModels[index];
                }
                else
                {
                    DiagramViewModel = DiagramViewModels.FirstOrDefault();
                }
            }

        }

        public void RenamePageExecuted(object para)
        {
            if (para is DiagramViewModel oldpage)
            {
                oldpage.IsEditName = true;
            }
        }

        public void EndRenamePageExecuted(object para)
        {
            if (para is DiagramViewModel oldpage)
            {
                oldpage.IsEditName = false;
            }
        }

        public void AddImageExecuted(object para)
        {
            ImageItemViewModel itemBase = new ImageItemViewModel();
            DiagramViewModel?.AddItemCommand.Execute(itemBase);
            if (itemBase.Root != null)
            {
                _service.DrawModeViewModel.CursorMode = CursorMode.Move;
            }
        }

        public void EditImageExecuted(object para)
        {
            ImageItemViewModel itemBase = para as ImageItemViewModel;
            if (itemBase != null)
            {
                itemBase.EditData();
            }
        }

        public void ResizeImageExecuted(object para)
        {
            ImageItemViewModel itemBase = para as ImageItemViewModel;
            if (itemBase != null)
            {
                itemBase.StartResize();
            }
        }

        public void ResetImageExecuted(object para)
        {
            ImageItemViewModel itemBase = para as ImageItemViewModel;
            if (itemBase != null)
            {
                itemBase.Reset();
            }
        }

        public void AddVideoExecuted(object para)
        {
            VideoItemViewModel itemBase = new VideoItemViewModel();
            DiagramViewModel?.AddItemCommand.Execute(itemBase);
            if (itemBase.Root != null)
            {
                _service.DrawModeViewModel.CursorMode = CursorMode.Move;
            }
        }

        public void AddOutLineTextExecuted(object para)
        {
            OutLineTextDesignerItemViewModel itemBase = new OutLineTextDesignerItemViewModel();
            DiagramViewModel?.AddItemCommand.Execute(itemBase);
            if (itemBase.Root != null)
            {
                _service.DrawModeViewModel.CursorMode = CursorMode.Move;
            }
        }

        public void AddBarcodeExecuted(object para)
        {
            BarcodeDesignerItemViewModel itemBase = new BarcodeDesignerItemViewModel() { Format = (BarcodeFormat)Enum.Parse(typeof(BarcodeFormat), para.ToString()), Text = "AIStudio.Wpf.DiagramApp" };
            DiagramViewModel?.AddItemCommand.Execute(itemBase);
            if (itemBase.Root != null)
            {
                _service.DrawModeViewModel.CursorMode = CursorMode.Move;
            }
        }
        #endregion

        private Size MeasureString(OutLineTextDesignerItemViewModel itemBase)
        {
            var formattedText = new FormattedText(
                itemBase.Text,
                CultureInfo.CurrentUICulture,
                FlowDirection.LeftToRight,
                new Typeface(new FontFamily(itemBase.FontViewModel.FontFamily), itemBase.FontViewModel.FontStyle, itemBase.FontViewModel.FontWeight, itemBase.FontViewModel.FontStretch),
                itemBase.FontViewModel.FontSize,
                Brushes.Black);

            return new Size(formattedText.Width, formattedText.Height);
        }

        public virtual void Dispose()
        {
        }
    }
}
